/* parser for a simple math expression language */
%{
#include <stdio.h>
#include <math.h>
#include "expr.h"
#include "expr_parser_types.h"

void pcg_error(pcg_parse_parm_t *parm, void *scanner, char *err);

#define YY_EXTRA_TYPE pcg_parse_parm_t *

%}

%pure_parser
%parse-param {pcg_parse_parm_t *parm}
%parse-param {void *scanner}
%lex-param {pcgscan_t *scanner}

%union {
	pcg_expr_t *eval;
	pcg_exprlist_t *elval;
	char *aval;
	double dval;
}

%left AND OR
%left LT GT 
%left PLUS MINUS
%left MULT DIV
%nonassoc UMINUS
%nonassoc UNOT
%right POW

%token <aval> ID 
%token <dval> NUMBER
%token LPAREN RPAREN PLUS MULT MINUS DIV POW COMMA GT LT OR AND NOT 
%type <eval> expr
%type <elval> expr_list

%%

filter:		/* nothing */
	|	expr	{ parm->result = $1; }
	;
	
expr:	  LPAREN expr RPAREN	{ $$ = $2; }
		| expr PLUS expr		{ $$ = pcg_expr_create(EXP_PLUS, $1, $3); }
		| expr MINUS expr		{ $$ = pcg_expr_create(EXP_MINUS, $1, $3); }
		| expr MULT expr		{ $$ = pcg_expr_create(EXP_MULT, $1, $3);  }
		| expr DIV expr			{ $$ = pcg_expr_create(EXP_DIV, $1, $3); }
		| expr GT expr			{ $$ = pcg_expr_create(EXP_GT, $1, $3); }
		| expr LT expr			{ $$ = pcg_expr_create(EXP_LT, $1, $3); }
		| expr AND expr			{ $$ = pcg_expr_create(EXP_AND, $1, $3); }
		| expr OR expr			{ $$ = pcg_expr_create(EXP_OR, $1, $3); }
		| MINUS expr %prec UMINUS { $$ = pcg_expr_create(EXP_UMINUS, $2); }
		| NOT expr %prec UNOT	{ $$ = pcg_expr_create(EXP_NOT, $2); }
		| expr POW expr			{ $$ = pcg_expr_create(EXP_POW, $1, $3); }
		| ID					{ $$ = pcg_expr_create(EXP_ID, $1); free($1); }
		| ID LPAREN expr_list RPAREN	{ $$ = pcg_expr_create(EXP_FUNC, $1, $3); free($1); }
		| NUMBER				{ $$ = pcg_expr_create(EXP_NUMBER, $1);  }
	;
	
expr_list:	expr		{ $$ = pcg_exprlist_create($1); }
		| expr COMMA expr_list		{ $$ = pcg_exprlist_cons($3, $1); }
	;


%%

void pcg_error(pcg_parse_parm_t *parm, void *scanner, char *err) {
	fprintf(stderr, "%s\n", err);
}